package com.auntvt.timo.jpax.core.quick.matcher;

import com.auntvt.timo.jpax.core.exception.JpaxException;
import com.auntvt.timo.jpax.core.quick.Quick;
import com.auntvt.timo.jpax.core.quick.enums.CriteriaType;
import com.auntvt.timo.jpax.core.quick.enums.MatcherJoinType;
import com.auntvt.timo.jpax.core.quick.enums.MatcherType;
import com.auntvt.timo.jpax.core.quick.interfaces.QuickSpecification;
import com.auntvt.timo.jpax.core.quick.model.Matcher;
import com.auntvt.timo.jpax.core.quick.node.CriteriaNode;
import com.auntvt.timo.jpax.core.quick.node.MatcherNode;
import org.springframework.util.StringUtils;

import javax.persistence.Entity;
import javax.persistence.criteria.CriteriaBuilder;
import javax.persistence.criteria.CriteriaQuery;
import javax.persistence.criteria.Predicate;
import javax.persistence.criteria.Root;
import java.lang.reflect.Field;
import java.util.*;
import java.util.function.Consumer;
import java.util.function.Supplier;
import java.util.stream.Collectors;

/**
 * QuickSpecification 条件匹配器抽象类
 *
 * @author 小懒虫 <auntvt@163.com>
 * @date 2021/7/31
 */
public abstract class AbstractMatcher<T, Children, R> implements QuickSpecification<T> {

    /**
     * 默认的条件类型（等于）
     */
    private final static CriteriaType DEFAULT_TYPE = CriteriaType.EQ;
    /**
     * 多个值分隔符
     */
    private final static String MULTIPLE_VALUE_SPLIT = ",";
    /**
     * 联表字段名分割符号
     */
    private static final String JOIN_FIELD_NAME_SPLIT = ".";
    /**
     * this变量
     */
    @SuppressWarnings("unchecked")
    private final Children that = (Children) this;
    /**
     * 匹配器内置Quick对象
     */
    protected Quick<T> quick = new MatcherQuick<>();
    /**
     * 字段匹配列表
     */
    protected Map<String, MatcherNode> matchers = new LinkedHashMap<>();
    /**
     * 忽略字段列表
     */
    protected List<String> ignores = new ArrayList<>();
    /**
     * 参数对象（Bean对象/Matcher对象）
     */
    protected Object params;

    /**
     * 带参构造函数，必须传入参数对象（Bean对象/Matcher对象）
     *
     * @param params 参数对象（Bean对象/Matcher对象）
     */
    public AbstractMatcher(Object params) {
        if (params == null) {
            throw new JpaxException("匹配器对象不能为空！");
        }
        if (params instanceof Matcher) {
            Matcher matcher = (Matcher) params;
            this.params = matcher.getParams();
            if ((matcher.getRules() != null)) {
                Map<String, MatcherNode> toRules = matcher.getRules().entrySet().stream()
                        .collect(Collectors.toMap(
                                Map.Entry::getKey,
                                item -> new MatcherNode(item.getKey(), item.getValue().getType())
                        ));
                matchers.putAll(toRules);
            }
        } else {
            this.params = params;
        }
    }

    /**
     * 子类创建一个本身的实例对象
     */
    protected abstract Children instance(Object params);

    /**
     * 将字段名称对象格式化为字符串类型，由子类负责实现
     *
     * @param fieldName 字段名称对象
     * @return 字段名
     */
    protected abstract String fieldNameToString(R fieldName);

    /**
     * 获取内置Quick对象
     */
    public Quick<T> getQuick() {
        return quick;
    }

    /**
     * 字段条件匹配
     *
     * @param fieldName   字段名
     * @param matcherType 条件类型
     * @return this
     */
    public Children withMatcher(R fieldName, MatcherType matcherType) {
        String name = fieldNameToString(fieldName);
        matchers.put(name, new MatcherNode(name, matcherType.getType()));
        return that;
    }

    /**
     * 表关联条件匹配（默认内连接INNER）
     *
     * @param fieldName 字段名
     * @param consumer  可使用箭头函数
     * @return this
     */
    public Children withJoinMatcher(R fieldName, Consumer<? extends AbstractMatcher> consumer) {
        return withJoinMatcher(fieldName, MatcherJoinType.JOIN_INNER, consumer);
    }

    /**
     * 表关联条件匹配
     *
     * @param fieldName       字段名
     * @param matcherJoinType 条件类型
     * @param consumer        可使用箭头函数
     * @return this
     */
    public Children withJoinMatcher(R fieldName, MatcherJoinType matcherJoinType, Consumer<? extends AbstractMatcher> consumer) {
        String name = fieldNameToString(fieldName);
        matchers.put(name, new MatcherNode(name, matcherJoinType.getType(), consumer));
        return that;
    }

    /**
     * 忽略字段，不对忽略的字段进行匹配查询操作
     *
     * @param fieldNames 字段名数组
     * @return this
     */
    public Children ignores(String... fieldNames) {
        ignores.addAll(Arrays.asList(fieldNames));
        return that;
    }

    /**
     * 忽略字段，不对忽略的字段进行匹配查询操作
     *
     * @param fieldNames 字段名列表
     * @return this
     */
    public Children ignores(List<R> fieldNames) {
        for (R fieldName : fieldNames) {
            ignores.add(fieldNameToString(fieldName));
        }
        return that;
    }

    /**
     * 转换为其他实现方式
     *
     * @param matcher AbstractMatcher子对象
     * @param <M>     AbstractMatcher子类
     * @return AbstractMatcher实现类
     */
    protected <M extends AbstractMatcher> M convert(M matcher) {
        matcher.quick = this.quick;
        matcher.matchers = this.matchers;
        matcher.ignores = this.ignores;
        return matcher;
    }

    @Override
    public Predicate toPredicate(Root<T> root, CriteriaQuery<?> query, CriteriaBuilder criteriaBuilder) {
        fillQuick((MatcherQuick<T>) this.quick, buildMatcherNodes(null));
        return quick.toPredicate(root, query, criteriaBuilder);
    }

    /**
     * 构建CriteriaNodes列表
     *
     * @param matcherNodes 节点列表
     */
    private void fillQuick(MatcherQuick<?> mQuick, List<MatcherNode> matcherNodes) {
        for (MatcherNode node : matcherNodes) {
            // 添加当前字段判断条件
            String name = node.getFieldName();
            CriteriaType type = node.getType();
            Object value = node.getValue();
            switch (type) {
                case BETWEEN:
                case NOT_BETWEEN:
                    // 多个值条件判断
                    String[] multipleValue = String.valueOf(value).split(MULTIPLE_VALUE_SPLIT);
                    if (multipleValue.length < 2) {
                        throw new JpaxException("匹配条件为：BETWEEN、NOT_BETWEEN的类型，值必须使用“,”逗号隔开，且必须为两个值！");
                    }
                    mQuick.addNode(true, type, name, multipleValue);
                    break;
                case IN:
                case NOT_IN:
                    // 多个值条件判断
                    value = String.valueOf(value).split(MULTIPLE_VALUE_SPLIT);
                    mQuick.addNode(true, type, name, value);
                    break;
                case JOIN_INNER:
                case JOIN_LEFT:
                case JOIN_RIGHT:
                    MatcherQuick<?> quickMatcher = new MatcherQuick<>();
                    fillQuick(quickMatcher, node.getChildren());
                    mQuick.addJoin(true, type, name, quickMatcher.getCriteriaNodes());
                    break;
                default:
                    mQuick.addNode(true, type, name, value);
            }
        }
    }

    /**
     * 构建MatcherNodes列表
     *
     * @param superName 上级实体类关联字段名
     */
    @SuppressWarnings("unchecked")
    private List<MatcherNode> buildMatcherNodes(String superName) {
        List<MatcherNode> nodes = new ArrayList<>();
        // 判断参数对象类型判断是否为Map对象，是则使用Map对象数据，否则使用bean对象数据
        if (params instanceof Map) {
            ((Map<String, Object>) params).forEach((name, value) -> {
                MatcherNode node = buildMatcherNode(superName, name, null, () -> value);
                if (node != null) {
                    nodes.add(node);
                }
            });
        } else {
            // 获取字段列表
            Field[] fields = params.getClass().getDeclaredFields();
            for (Field field : fields) {
                String name = field.getName();
                Class<?> type = field.getType();
                MatcherNode node = buildMatcherNode(superName, name, type, () -> {
                    try {
                        field.setAccessible(true);
                        return field.get(params);
                    } catch (IllegalAccessException e) {
                        throw new JpaxException(e);
                    }
                });
                if (node != null) {
                    nodes.add(node);
                }
            }
        }
        return nodes;
    }

    /**
     * 构建MatcherNode节点
     *
     * @param superName 上级实体类关联字段名
     * @param name      字段名称
     * @param type      字段类型
     * @param value     值回调方法
     */
    @SuppressWarnings("unchecked")
    private MatcherNode buildMatcherNode(String superName, String name, Class<?> type, Supplier<Object> value) {
        String fullName = (StringUtils.hasText(superName) ? superName + JOIN_FIELD_NAME_SPLIT : "") + name;
        // 判断是否为忽略的字段
        if (ignores.contains(name) || ignores.contains(fullName)) {
            return null;
        }

        // 判断值是否为null
        if (value.get() == null) {
            return null;
        }

        // 判断是否为关联实体类
        MatcherNode node;
        if (type != null && type.isAnnotationPresent(Entity.class)) {
            Children children = instance(value.get());
            if (matchers == null) {
                return null;
            }

            // 关联类型，默认为内关联INNER
            CriteriaType joinType = CriteriaType.JOIN_INNER;

            // 执行Consumer对象
            if (matchers.containsKey(name)) {
                MatcherNode joinNode = matchers.get(name);
                joinType = joinNode.getType();
                Consumer<Children> consumer = (Consumer<Children>) joinNode.getConsumer();
                if (consumer != null) {
                    consumer.accept(children);
                }
            }
            AbstractMatcher joinMatcher = (AbstractMatcher) children;
            // 将上级节点名称带有“.”关联符号的匹配规则列表和忽略列表合并到关联适配器中
            joinMatcher.matchers.putAll(
                    matchers.entrySet().stream().filter(n -> n.getKey().startsWith(fullName + JOIN_FIELD_NAME_SPLIT))
                            .collect(Collectors.toMap(Map.Entry::getKey, Map.Entry::getValue))
            );
            joinMatcher.ignores.addAll(
                    ignores.stream().filter(n -> n.startsWith(fullName + JOIN_FIELD_NAME_SPLIT))
                            .collect(Collectors.toList())
            );
            node = new MatcherNode(name, joinType);
            node.setChildren(joinMatcher.buildMatcherNodes(fullName));
        }

        /* 获取当前的条件节点，并判断当前字段是否定义匹配规则
         * 关联查询：嵌套模式优先级比名称带有关联符号的匹配规则高
         */
        else if (matchers.containsKey(name)) {
            node = matchers.get(name);
        } else if (matchers.containsKey(fullName)) {
            node = matchers.get(fullName);
            node.setFieldName(name);
        } else {
            node = new MatcherNode(name, DEFAULT_TYPE);
        }
        node.setValue(value.get());
        return node;
    }

    /**
     * 重写Quick类，使其开放部分接口
     */
    private class MatcherQuick<S> extends Quick<S> {

        /**
         * 获取CriteriaNode列表
         */
        List<CriteriaNode> getCriteriaNodes() {
            return this.criteriaNodes;
        }

        @Override
        protected Quick<S> addNode(boolean condition, CriteriaType criteriaType, String fieldName, Object value) {
            return super.addNode(condition, criteriaType, fieldName, value);
        }

        @Override
        protected Quick<S> addAtom(boolean condition, CriteriaType criteriaType, Consumer<Quick<S>> consumer) {
            return super.addAtom(condition, criteriaType, consumer);
        }

        @Override
        protected Quick<S> addJoin(boolean condition, CriteriaType criteriaType, String fieldName, List<CriteriaNode> children) {
            return super.addJoin(condition, criteriaType, fieldName, children);
        }
    }
}
